package cn.trigram.date;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Period;
import java.time.ZoneId;
import java.util.Date;
import java.util.Objects;

/**
 * 时间日期调整器，使用链式调用。
 * 调整结束后必须调用{@link #toLocalDateTime()}/{@link #toLocalDate()}/{@link #toLocalTime()} 方法
 *
 * @version 1.0.0
 * @date 2022/07/22 14:32:57
 * @since 1.0.0
 */
public class DateTimeAdjuster {

    private LocalDateTime ldt;

    public DateTimeAdjuster(Date d) {

        Objects.requireNonNull(d, "日期参数不能为空");
        this.ldt = LocalDateTime.ofInstant(d.toInstant(), ZoneId.systemDefault());
    }

    public DateTimeAdjuster(LocalDateTime ldt) {

        Objects.requireNonNull(ldt, "日期参数不能为空");
        this.ldt = ldt;
    }

    public DateTimeAdjuster(LocalDate ld) {

        Objects.requireNonNull(ld, "日期参数不能为空");
        this.ldt = ld.atStartOfDay();
    }

    public DateTimeAdjuster(LocalTime lt) {

        Objects.requireNonNull(lt, "日期参数不能为空");
        this.ldt = lt.atDate(LocalDate.now());
    }

    /**
     * 增加年月日，时分秒的单位时间
     *
     * @param period   年月日
     * @param duration 时分秒
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster plus(Period period, Duration duration) {

        Objects.requireNonNull(period, "周期period不能为空");
        Objects.requireNonNull(duration, "时间段duration不能为空");
        this.ldt = this.ldt.plus(period).plus(duration);
        return this;
    }

    /**
     * 增加年
     *
     * @param year
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster plusYear(int year) {

        this.ldt = this.ldt.plus(Period.ofYears(year));
        return this;
    }

    /**
     * 增加月
     *
     * @param month
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster plusMonth(int month) {

        this.ldt = this.ldt.plus(Period.ofMonths(month));
        return this;
    }

    /**
     * 增加日
     *
     * @param day
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster plusDay(int day) {

        this.ldt = this.ldt.plus(Period.ofDays(day));
        return this;
    }

    /**
     * 增加时
     *
     * @param hour
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster plusHour(int hour) {

        this.ldt = this.ldt.plus(Duration.ofHours(hour));
        return this;
    }

    /**
     * 增加分
     *
     * @param minute
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster plusMinute(int minute) {

        this.ldt = this.ldt.plus(Duration.ofMinutes(minute));
        return this;
    }

    /**
     * 增加秒
     *
     * @param second
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster plusSecond(int second) {

        this.ldt = this.ldt.plus(Duration.ofSeconds(second));
        return this;
    }

    /**
     * 增加毫秒
     *
     * @param millis
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster plusMillis(int millis) {

        this.ldt = this.ldt.plus(Duration.ofMillis(millis));
        return this;
    }

    /**
     * 增加纳秒
     *
     * @param nanos
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster plusNanos(int nanos) {

        this.ldt = this.ldt.plus(Duration.ofNanos(nanos));
        return this;
    }

    /**
     * 减少年月日，时分秒
     *
     * @param period   年月日
     * @param duration 时分秒
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster minus(Period period, Duration duration) {

        Objects.requireNonNull(period, "周期period不能为空");
        Objects.requireNonNull(duration, "时间段duration不能为空");
        this.ldt = this.ldt.minus(period).minus(duration);
        return this;
    }

    /**
     * 减少年
     *
     * @param year
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster minusYear(int year) {

        this.ldt = this.ldt.minus(Period.ofYears(year));
        return this;
    }

    /**
     * 减少月
     *
     * @param month
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster minusMonth(int month) {

        this.ldt = this.ldt.minus(Period.ofMonths(month));
        return this;
    }

    /**
     * 减少日
     *
     * @param day
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster minusDay(int day) {

        this.ldt = this.ldt.minus(Period.ofDays(day));
        return this;
    }

    /**
     * 减少时
     *
     * @param hour
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster minusHour(int hour) {

        this.ldt = this.ldt.minus(Duration.ofHours(hour));
        return this;
    }

    /**
     * 减少分
     *
     * @param minute
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster minusMinute(int minute) {

        this.ldt = this.ldt.minus(Duration.ofMinutes(minute));
        return this;
    }

    /**
     * 减少秒
     *
     * @param second
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster minusSecond(int second) {

        this.ldt = this.ldt.minus(Duration.ofSeconds(second));
        return this;
    }

    /**
     * 减少毫秒
     *
     * @param millis
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster minusMillis(int millis) {

        this.ldt = this.ldt.minus(Duration.ofMillis(millis));
        return this;
    }

    /**
     * 减少纳秒
     *
     * @param nanos
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster minusNanos(int nanos) {

        this.ldt = this.ldt.minus(Duration.ofNanos(nanos));
        return this;
    }

    /**
     * 重置为一年之初
     *
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster resetAtStartOfYear() {

        this.ldt = this.ldt.withDayOfYear(1);
        this.resetAtStartOfDay();
        return this;
    }

    /**
     * 重置为一年之末
     *
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster resetAtEndOfYear() {

        this.ldt = this.ldt.plusYears(1L);
        this.resetAtStartOfYear();
        this.minusNanos(1);
        return this;
    }

    /**
     * 重置为一月之初
     *
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster resetAtStartOfMonth() {

        this.ldt = this.ldt.withDayOfMonth(1);
        this.resetAtStartOfDay();
        return this;
    }

    /**
     * 重置为一月之末
     *
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster resetAtEndOfMonth() {

        this.ldt = this.ldt.plusMonths(1L);
        this.resetAtStartOfMonth();
        this.minusNanos(1);
        return this;
    }

    /**
     * 重置为一周之初
     *
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster resetAtStartOfWeek() {

        this.ldt = this.ldt.minusDays(this.ldt.getDayOfWeek().getValue()).plusDays(1);
        this.resetAtStartOfDay();
        return this;
    }

    /**
     * 重置为一周之末
     *
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster resetAtEndOfWeek() {

        this.ldt = this.ldt.minusDays(this.ldt.getDayOfWeek().getValue()).plusDays(7);
        this.resetAtEndOfDay();
        return this;
    }

    /**
     * 重置为一日之初
     *
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster resetAtStartOfDay() {

        this.ldt = this.ldt.toLocalDate().atStartOfDay();
        return this;
    }

    /**
     * 重置为一日之末
     *
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster resetAtEndOfDay() {

        this.ldt = this.ldt.withHour(23);
        this.resetAtEndOfHour();
        return this;
    }

    /**
     * 重置为时辰之初
     *
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster resetAtStartOfHour() {

        this.ldt = this.ldt.withMinute(0);
        this.resetAtStartOfMinutes();
        return this;
    }

    /**
     * 重置为时辰之末
     *
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster resetAtEndOfHour() {

        this.ldt = this.ldt.withMinute(59);
        this.resetAtEndOfMinutes();
        return this;
    }

    /**
     * 重置为时刻之初
     *
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster resetAtStartOfMinutes() {

        this.ldt = this.ldt.withSecond(0);
        this.resetAtStartOfSecond();
        return this;
    }

    /**
     * 重置为时刻之末
     *
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster resetAtEndOfMinutes() {

        this.ldt = this.ldt.withSecond(59);
        this.resetAtEndOfSecond();
        return this;
    }

    /**
     * 重置为刹那之初
     *
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster resetAtStartOfSecond() {

        this.ldt = this.ldt.withNano(0);
        return this;
    }

    /**
     * 重置为刹那之末
     *
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster resetAtEndOfSecond() {

        this.ldt = this.ldt.withNano(999999999);
        return this;
    }

    /**
     * 重置为季度之初
     *
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster resetAtStartOfQuarter() {

        this.ldt = this.ldt.withMonth(this.ldt.getMonth().firstMonthOfQuarter().getValue())
                .withDayOfMonth(1);
        this.resetAtStartOfDay();
        return this;
    }

    /**
     * 重置为季度之末
     *
     * @return {@link DateTimeAdjuster }
     */
    public DateTimeAdjuster resetAtEndOfQuarter() {

        this.ldt = this.ldt.withMonth(this.ldt.getMonth().firstMonthOfQuarter().getValue())
                .withDayOfMonth(1);
        this.plusMonth(3);
        this.minusDay(1);
        this.resetAtEndOfDay();
        return this;
    }

    /**
     * 调整完成后必须调用此方法获取调整后的日期时间
     *
     * @return {@link LocalDateTime }
     */
    public LocalDateTime toLocalDateTime() {

        return this.ldt;
    }

    /**
     * 调整完成后必须调用此方法获取调整后的日期
     *
     * @return {@link LocalDate }
     */
    public LocalDate toLocalDate() {

        return this.ldt.toLocalDate();
    }

    /**
     * 调整完成后必须调用此方法获取调整后的时间
     *
     * @return {@link LocalTime }
     */
    public LocalTime toLocalTime() {

        return this.ldt.toLocalTime();
    }

    @Override
    public String toString() {
        return this.ldt.toString();
    }
}
